
package ploy.lang;

import java.util.*;
import java.io.*;
import java.math.*;

/**
 * DynamicObject represents the superclass of all dynamicly-typed objects
 * in the Ploy runtime.
 */
public abstract class DynamicObject {

    public static String jvmClass = "ploy/lang/DynamicObject";
    public static String jvmType = "L" + jvmClass + ";";    

    /**
     * Generates a string representation of the dynamic object.  Not
     * guaranteed to be re-parseable.
     */
    public abstract String toString( );

    /* The following are typing predicates. */
    public boolean isPair() { return false; }
    public boolean isNil() { return false; }
    public boolean isSymbol() { return false; }
    public boolean isNumber() { return false; }
    public boolean isClosure() { return false; }
    public boolean isString() { return false; }
		public boolean isDynamicClassObject() { return false; }

    /* The following are static reading functions. */

    /** Reads whitespace, and returns nothing. */
    static private void eatWhitespace( PushbackReader r ) 
	throws IOException 
    {
	char c;
	do {
	    c = (char)r.read();
	} while ( ( c != -1 ) && ( Character.isWhitespace( c ) ) );
	if ( c != -1 ) r.unread( c );
    }

    /** 
     * Reads the next characters as a symbol.  If it cannot read a character
     * as a symbol, it returns everything it has so far. 
     */
    static private DynamicObject readSymbol( PushbackReader r ) 
	throws IOException 
    {
	char c = (char)r.read();
	if ( !Character.isJavaIdentifierStart( c ) ) 
	    throw new IOException ( "Bad symbol" );
	String s = "";
	while ( ( c != -1 ) && 
		( ( Character.isJavaIdentifierPart( c ) ) ||
		  ( Character.getType( c ) == Character.DASH_PUNCTUATION ) ||
		  ( Character.getType( c ) == Character.OTHER_PUNCTUATION ) ) ) {
	    s += c; /* INEFFICIENT */
	    c = (char)r.read();
	}
	if ( c != -1 ) r.unread( c );
	return new Symbol( s );
    }

    /** 
     * Reads the next characters as a number.  If it cannot read a character
     * as a number, it returns everything it has so far. 
     */
    static private DynamicObject readNumber( PushbackReader r ) 
	throws IOException 
    {
	char c = (char)r.read();
	String s = "";
	while ( ( c != -1 ) && ( Character.isDigit( c ) ) ) {
	    s += c; /* INEFFICIENT */
	    c = (char)r.read();
	}
	if ( c != -1 ) r.unread( c );
	return new Number( s );
    }
    
    static private DynamicObject readString( PushbackReader r )
	throws IOException
    {
	char c = (char)r.read();
	String s = "";
	while ( ( c != -1 ) && ( c != '"' ) ) {
	    s += c; /* INEFFICIENT */
	    c = (char)r.read();
	}
	if ( c == -1 )
	    throw new IOException( "Unexpected end-of-file" );
	return new DString( s );
    }

    /**
     * Starts reading a list.  May call other readers recursively.
     */
    static private DynamicObject readList( PushbackReader r ) 
	throws IOException 
    {
	eatWhitespace( r );
	char c = (char)r.read();
	if ( c == -1 ) {
	    throw new IOException( "Unexpected end-of-file" );
	} else if ( c == ')' ) {
	    return new Nil();
	}
	r.unread( c );
	DynamicObject first = readSexp( r );
	eatWhitespace( r );
	c = (char)r.read();
	DynamicObject second;

	if ( c == -1 ) {
	    throw new IOException( "Unexpected end-of-file" );
	} else if ( c == ')' ) {
	    second = new Nil();
	} else {
	    r.unread( c );
	    second = readList( r );
	}

	return new Pair( first, second );
    }
	
    /**
     * Reads any s-expression from the PushbackReader.
     */
    static public DynamicObject readSexp( PushbackReader r ) 
	throws IOException 
    {
	eatWhitespace( r );
	char c = (char)r.read();
	
	if ( c == -1 ) {
	    throw new IOException( "Unexpected end-of-file" );
	} else if ( c == '(' ) {
	    return readList( r );
	} else if ( c == ')' ) {
	    throw new IOException( "Extraneous close parenthesis" );
	} else if ( Character.isDigit( c ) ) {
	    r.unread( c );
	    return readNumber( r );
	} else if ( c == '"' ) {
	    return readString( r );
	} else {
	    r.unread( c );
	    return readSymbol( r );
	}
    }

    /* The following are abstract functions on dynamically-typed objects. */

    /**
     * Invokes this dynamically-typed object as if it were a function.  
     * If it is not a function, this should throw a runtime error.
     */
    public DynamicObject invoke0( ) 
	throws RuntimeError
    {
	throw new RuntimeError( "Attempt to invoke non-function" );
    }

    public DynamicObject invoke1( DynamicObject s1 ) 
	throws RuntimeError
    {
	throw new RuntimeError( "Attempt to invoke non-function" );
    }

    public DynamicObject invoke2( DynamicObject s1, DynamicObject s2 ) 
	throws RuntimeError
    {
	throw new RuntimeError( "Attempt to invoke non-function" );
    }

    public DynamicObject invoke3( DynamicObject s1, DynamicObject s2, 
				  DynamicObject s3  ) 
	throws RuntimeError
    {
	throw new RuntimeError( "Attempt to invoke non-function" );
    }

    public DynamicObject invoke4( DynamicObject s1, DynamicObject s2, 
				  DynamicObject s3, DynamicObject s4 ) 
	throws RuntimeError
    {
	throw new RuntimeError( "Attempt to invoke non-function" );
    }

    public DynamicObject invokeN( DynamicObject sn ) 
	throws RuntimeError
    {
	throw new RuntimeError( "Attempt to invoke non-function" );
    }

}
